package org.anno.chunkdb;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

/**
 * Visitor interface for a {@link Metadata.Response} with do-nothing method implementations.
 */
public class ResponseVisitor {

  public static ResponseVisitor getCsvStringBuilder() {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    return new Csv(out) {
      @Override
      public String toString() {
        try {
          return out.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
          throw new AssertionError(e);
        }
      }
    };
  }

  /**
   * Called for every response column with values that are suitable for a header. The label is the
   * column name or an aggregation expression.
   */
  public void visitColumn(int index, String label) {
  }

  /**
   * Called to give this visitor a chance to iterate through all rows. This is not a true iterator
   * over rows, because there may well be just one {@link Host} instance whose state changes with
   * every {@link java.util.Iterator#next()}} call.
   */
  public void visitRows(Iterable<Host<RowVisitor>> rows) {
  }

  /**
   * Writes response as HTML to output.
   */
  public static class Html extends ResponseVisitor {

    private final RowVisitor.Html rowVisitor;
    private final OutputStreamWriter writer;
    private final int orderByColumn;
    private final boolean ascending;

    /**
     * @param orderByColumn Column index that should be rendered as ordered. -1 means none.
     * @param ascending     Whether ordering is ascending or descending
     */
    public Html(OutputStream out, int orderByColumn, boolean ascending) throws IOException {
      this.orderByColumn = orderByColumn;
      this.ascending = ascending;
      rowVisitor = new RowVisitor.Html(out);
      writer = new OutputStreamWriter(out);
      writer.append("<table class='pure-table pure-table-striped'>\n<thead>\n  <tr>");
    }

    @Override
    public void visitColumn(int index, String label) {
      try {
        writer.append("<th><a href='?orderBy=").append(Integer.toString(index))
            .append("&ascending=").append(ascending && index == orderByColumn ? "false" : "true")
            .append("'>").append(label);
        if (index == orderByColumn) {
          writer.append("&nbsp;").append(ascending ? "&#9652;" : "&#9662;");
        }
        writer.append("</a></th>");
      } catch (IOException e) {
        throw new IllegalStateException(e);
      }
    }

    @Override
    public void visitRows(Iterable<Host<RowVisitor>> rows) {
      try {
        writer.append("</tr></thead><tbody>");
        for (Host<RowVisitor> row : rows) {
          writer.append("<tr>").flush();
          row.accept(rowVisitor);
          writer.append("</tr>\n");
        }
        writer.append("</tbody></table>").flush();
      } catch (IOException e) {
        throw new IllegalStateException(e);
      }
    }
  }

  static class Csv extends ResponseVisitor {

    private final RowVisitor.Csv rowVisitor;
    private final OutputStream out;

    public Csv(OutputStream out) {
      this.out = out;
      rowVisitor = new RowVisitor.Csv(out);
    }

    @Override
    public void visitColumn(int index, final String label) {
      final byte[] bytes;
      try {
        bytes = label.getBytes("UTF-8");
      } catch (UnsupportedEncodingException e) {
        throw new AssertionError(e);
      }
      rowVisitor.visit(index, Metadata.Column.Type.STRING,
                       visitor -> visitor.visitString(bytes, 0, bytes.length));
    }

    @Override
    public void visitRows(Iterable<Host<RowVisitor>> rows) {
      for (Host<RowVisitor> row : rows) {
        try {
          out.write('\n');
        } catch (IOException e) {
          throw new IllegalStateException(e);
        }
        row.accept(rowVisitor);
      }
    }
  }
}
